#include "led_side_pwm.h"
#include "sys_user.h"

static volatile frame_run_status_t frame_status;

void led_side_set_status(uint8_t enable) {
    color_t color = {
        .value = 0,
    };
    led_side_set_color(color);

    if (enable) {
        FL_ATIM_Enable(ATIM);
        sys_clock_delay_ms(1);
        FL_ATIM_EnableALLOutput(ATIM);

        FL_GPTIM_EnableIT_Update(GPTIM0);
        FL_GPTIM_Enable(GPTIM0);
    } else {
        FL_ATIM_DisableALLOutput(ATIM);
        FL_ATIM_Disable(ATIM);

        FL_GPTIM_DisableIT_Update(GPTIM0);
        FL_GPTIM_Disable(GPTIM0);
    }
}

void led_side_set_color(color_t color) {
    static color_t color_last = {
        .value = 0xffffff,
    };
    if (color.value == color_last.value) {
        return ;
    }
    color_last.value = color.value;
    FL_ATIM_WriteCompareCH1(ATIM, color.b);
    FL_ATIM_WriteCompareCH2(ATIM, color.r);
    FL_ATIM_WriteCompareCH3(ATIM, color.g);
}

void led_side_select_frame(const frame_t* frame) {
    if (frame_status.list == frame) {
        return ;
    }

    __disable_irq();
    frame_status.list = frame;
    frame_status.count_total = FRAME_MAX_COUNT;
    frame_status.run_ms = 0;
    frame_status.color_last = frame_status.color_now;
    frame_status.index = 0;
    __enable_irq();
}

void led_side_fade_to_color(color_t color, uint32_t time_ms) {
    static frame_t frames[2];
    static uint8_t index = 0;
    frame_t* frame = &frames[index];
    index = 1 - index;

    frame->color = color;
    frame->diff_ms = time_ms;
    frame->total_ms = time_ms;
    frame->is_last = 1;

    led_side_select_frame(frame);
}

void led_side_color_update() {
    if (frame_status.run_ms >= frame_status.list[frame_status.index].total_ms) {
        frame_status.color_last = frame_status.list[frame_status.index].color;
        if (frame_status.list[frame_status.index].is_last) {
            frame_status.index = 0;
        } else {
            frame_status.index += 1;
            if (frame_status.index >= frame_status.count_total) {
                frame_status.index = 0;
            }
        }
        frame_status.run_ms = 0;
    }

    const frame_t* frame = &frame_status.list[frame_status.index];
    color_t color_now = {
        .value = 0,
    };

    if (frame_status.run_ms >= frame->diff_ms) {
        color_now = frame->color;
    } else {
        color_now.r = (frame_status.run_ms * frame->color.r + (frame->diff_ms - frame_status.run_ms) * frame_status.color_last.r) / frame->diff_ms;
        color_now.g = (frame_status.run_ms * frame->color.g + (frame->diff_ms - frame_status.run_ms) * frame_status.color_last.g) / frame->diff_ms;
        color_now.b = (frame_status.run_ms * frame->color.b + (frame->diff_ms - frame_status.run_ms) * frame_status.color_last.b) / frame->diff_ms;
    }

    frame_status.run_ms += 10;
    if (frame_status.run_ms > frame->total_ms) {
        frame_status.run_ms = frame->total_ms;
    }
    led_side_set_color(color_now);
    frame_status.color_now = color_now;
}

void GPTIM0_IRQHandler() {
    if(FL_GPTIM_IsEnabledIT_Update(GPTIM0) && FL_GPTIM_IsActiveFlag_Update(GPTIM0)) {
        FL_GPTIM_ClearFlag_Update(GPTIM0);
    }
    led_side_color_update();
}
